package jwt

import (
	"errors"
	"github.com/dgrijalva/jwt-go"
	"go-gin-demo/settings"
	"time"
)

const AccessTokenExpireDuration = time.Hour * 2
const RefreshTokenExpireDuration = time.Hour * 24 * 7

type MyClaims struct {
	UserID int64 `json:"user_id"`
	jwt.StandardClaims
}

// keyFunc 解析 token 时需要引入的返回密钥的函数
func keyFunc(*jwt.Token) (interface{}, error) {
	return settings.Conf.JWTSecret, nil
}

// GenToken 生成token
func GenToken(userID int64) (aToken, rToken string, err error) {
	c := MyClaims{
		userID,
		jwt.StandardClaims{
			ExpiresAt: time.Now().Add(AccessTokenExpireDuration).Unix(),
			Issuer:    settings.Conf.JWTIssuer,
		},
	}

	// 使用指定的签名方法创建签名对象
	aToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, c).SignedString(settings.Conf.JWTSecret)
	if err != nil {
		return
	}

	// refresh token 不需要自定义数据信息
	rToken, err = jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.StandardClaims{
		ExpiresAt: time.Now().Add(RefreshTokenExpireDuration).Unix(),
		Issuer:    settings.Conf.JWTIssuer,
	}).SignedString(settings.Conf.JWTSecret)

	return
}

// ParseToken 解析token
func ParseToken(tokenString string) (claims *MyClaims, err error) {
	var token *jwt.Token
	claims = new(MyClaims)
	token, err = jwt.ParseWithClaims(tokenString, claims, keyFunc)
	if err != nil {
		return nil, err
	}
	if token.Valid {
		return claims, nil
	}
	return nil, errors.New("invalid token")
}

// RefreshToken 更新 access token，不返回新的 refresh token，强制 refresh token 到期就必须重新登录
func RefreshToken(aToken, rToken string) (newAToken string, err error) {
	// refresh token 无效直接返回空
	if _, err = jwt.Parse(rToken, keyFunc); err != nil {
		return
	}

	// 从旧 access token 中解析出 claims 数据
	var claims = new(MyClaims)
	_, err = jwt.ParseWithClaims(aToken, claims, keyFunc)
	v, _ := err.(*jwt.ValidationError)

	// 当 access token 是过期错误并且 refresh token 没有过期时就创建一个新的 access token
	if v != nil && v.Errors == jwt.ValidationErrorExpired {
		newAToken, _, err = GenToken(claims.UserID)
		return
	}
	return aToken, nil
}
